#pragma once

#include <iostream>
#include <pthread.h>
#include <queue>
#include <ctime>
#include <unistd.h>

#define NUM 32

template<typename T>
class BlockQueue{
  private:
    int num;
    std::queue<T> block_queue;
    pthread_mutex_t lock;
    pthread_cond_t full;
    pthread_cond_t empty;

  public:
    BlockQueue(int _num = NUM):num(_num)
    {
      pthread_mutex_init(&lock, nullptr);
      pthread_cond_init(&full, nullptr);
      pthread_cond_init(&empty, nullptr);
    }

    void Push(const T& in)
    {
      //加锁
      Lock();
      //防止函数调用失败或者伪唤醒发生
      while(IsFull()){
      //if(IsFull()){
        //等待
        Wait(full);
      }

      //插入数据
      block_queue.push(in);
      //解锁
      UnLock();

      //唤醒线程
      //WakeUp(empty);
      if(block_queue.size() >= num / 2)
        WakeUp(empty);
    }

    void Pop(T& out)
    {
      Lock();
      //防止函数调用失败或者伪唤醒发生
      while(IsEmpty()){
      //if(IsEmpty()){
        Wait(empty);
      }

      out = block_queue.front();
      block_queue.pop();
      UnLock();

      if(block_queue.size() < num / 2)
        WakeUp(full);
      //WakeUp(full);
    }

    void Lock()
    {
      pthread_mutex_lock(&lock);
    }

    void UnLock()
    {
      pthread_mutex_unlock(&lock);
    }

    void Wait(pthread_cond_t& cond)
    {
      pthread_cond_wait(&cond, &lock);
    }

    void WakeUp(pthread_cond_t& cond)
    {
      pthread_cond_signal(&cond);
    }

    bool IsEmpty()const
    {
      return block_queue.size() == 0 ? true : false;
    }
    
    bool IsFull()const 
    {
      return block_queue.size() == num ? true : false;
    }
    
    ~BlockQueue()
    {
      pthread_mutex_destroy(&lock);
      pthread_cond_destroy(&full);
      pthread_cond_destroy(&empty);
    }

};
